package com.park.common.id;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.UUID;

public class IdWorker {
	 /**
     * 起始时间戳 (2018-01-01) 
     */
	private final  Long twepoch = 1514736000000L; 
	/**
	 * 机器ID所占的位数 
	 */
	private  final  Long workerIdBits = 5L; 
	/**
	 * 数据标识ID所占的位数
	 */
	private  final Long datacenterIdBits = 5L; 
	/**
	 * 支持的最大机器ID,结果是31
	 */
	private  final Long maxWorkerId = -1L ^ (-1L << workerIdBits); //31
	/**
	 * 支持的最大数据标识ID,结果是31
	 */
	private  final Long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //31
	/**
	 * 毫秒内序列在id中所占的位数 
	 */
	private  final Long sequenceBits = 12L; 
	/**
	 * 机器ID向左移12位
	 */
	private  final Long workerIdShift = sequenceBits; //12
	/**
	 * 数据标识ID向左移17(12+5)位
	 */
	private  final  Long datacenterIdShift = sequenceBits + workerIdBits; //12+5=17
	/**
	 * 时间戳向左移22(12+5+5)位
	 */
	private  final Long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //12+5+5=22
	/**
	 * 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
	 */
	private  final Long sequenceMask = -1L ^ (-1L << sequenceBits); //4095
	/**
	 * 上次生成ID的时间戳
	 */
	private  Long lastTimestamp = -1L; 

	/**
	 * 毫秒内序列（0～4095）
	 */
	private  Long sequence = 0L; 
	/**
	 * 机器ID（0～31）
	 */
	private  Long workerId; 
	/**
	 * 数据标识ID（0～31）
	 */
	private  Long datacenterId; 

	 /**
	 * 构造函数
	 * @param workerId 工作ID (0~31)
	 * @param datacenterId 数据中心ID (0~31)
	 */
	public IdWorker(long workerId, long datacenterId) { 
		if (workerId > maxWorkerId || workerId < 0) { 
			throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); 
		} 
		if (datacenterId > maxDatacenterId || datacenterId < 0) { 
			throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0"); 
		} 
		this.workerId = workerId; 
		this.datacenterId = datacenterId; 
	} 
	public IdWorker() { 
		this.workerId = maxWorkerId; 
		this.datacenterId = maxDatacenterId; 
	} 

	/**
	 * 获得下一个ID (该方法是线程安全的)
	 * @return
	 */
	public synchronized long nextId() { 
		long timestamp = timeGen(); 
		if (timestamp < lastTimestamp) { 
			try {
				throw new Exception("Clock moved backwards.  Refusing to generate id for "+ (lastTimestamp - timestamp) + " milliseconds");
			} catch (Exception e) {
				e.printStackTrace();
			} 
		} 

		if (lastTimestamp == timestamp) {
			// 当前毫秒内，则+1 
			sequence = (sequence + 1) & sequenceMask; 
			if (sequence == 0) { 
				// 当前毫秒内计数满了，则等待下一秒 
				timestamp = tilNextMillis(lastTimestamp); 
			}
		} else { 
			sequence = 0L; 
		} 
		lastTimestamp = timestamp; 
		// ID偏移组合生成最终的ID，并返回ID 
		return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; 

	} 

	private  long tilNextMillis(final long lastTimestamp) { 
		long timestamp = timeGen(); 
		while (timestamp <= lastTimestamp) { 
			timestamp = timeGen(); 
		} 
		return timestamp; 
	} 

	/**
	 * 获取当前时间毫秒数
	 * @return
	 */
	private  long timeGen() {
		return System.currentTimeMillis(); 
	} 
	
	/**
	 * 简易生成ID的方法
	 */
	public synchronized Long getId() {
		//获取UUID
		String uuid = "$" +new Random().nextLong()+ UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()+"$";
		//生成后缀
		long suffix = Math.abs(uuid.hashCode() % 100000000);
		SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS");
		String time = sdf.format(new Date(System.currentTimeMillis()));
		//生成前缀
		long prefix = (new Random().nextLong()-Long.parseLong(time)) / 100000000000000L;
		if(prefix <0){
			prefix = (Long.parseLong(time) - new Random().nextLong()) / 100000000000000L;
		}
		return Long.valueOf(String.valueOf(prefix + suffix));
	}
}
